﻿using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Drawing;
using System.Linq;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;
using System.Threading;

namespace Argos.Activities
{
	internal partial class WaitForTestRun: Activity , IEventActivity , IActivityEventListener<QueueEventArgs>
	{

		public WaitForTestRun()
		{
			this.InitializeComponent();
		}

		public WaitForTestRun(string name) : base(name)
		{
			this.InitializeComponent();
		}



		protected override ActivityExecutionStatus Cancel(ActivityExecutionContext executionContext)
		{
			if (null == executionContext)
				throw new ArgumentNullException("executionContext");

			if (!this.IsEventActivity && this.SubscriptionId != Guid.Empty)
			{
				((IEventActivity)this).Unsubscribe(executionContext, this);
			}
			return ActivityExecutionStatus.Closed;
		}

		protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
		{
			if (null == executionContext)
				throw new ArgumentNullException("executionContext");

			if (this.IsEventActivity)
				return ActivityExecutionStatus.Closed;

			((IEventActivity)this).Subscribe(executionContext, this);
			this.IsEventActivity = false;
			return ActivityExecutionStatus.Executing;
		}

		protected override ActivityExecutionStatus HandleFault(ActivityExecutionContext executionContext, Exception exception)
		{
			return base.HandleFault(executionContext, exception);
		}

		protected override void Initialize(IServiceProvider provider)
		{
			base.Initialize(provider);
			this.SetValue(QueueNameProperty, Guid.NewGuid());
		}

		protected override void OnClosed(IServiceProvider provider)
		{
			this.RemoveProperty(SubscriptionIdProperty);
			this.RemoveProperty(IsEventActivityProperty);
		}


		#region private bool IsEventActivity { get; set; }

		private bool IsEventActivity
		{
			get { return (bool)GetValue(IsEventActivityProperty); }
			set { SetValue(IsEventActivityProperty, value); }
		}

		// Using a DependencyProperty as the backing store for IsEventActivity.  This enables animation, styling, binding, etc...
		private static readonly DependencyProperty IsEventActivityProperty =
			DependencyProperty.Register("IsEventActivity", typeof(bool), typeof(WaitForTestRun));
		
		#endregion

		#region private Guid SubscriptionId { get; set; }

		private static DependencyProperty SubscriptionIdProperty = DependencyProperty.Register("SubscriptionId", typeof(Guid), typeof(WaitForTestRun), new PropertyMetadata(Guid.NewGuid()));

		private Guid SubscriptionId
		{
			get { return (Guid)base.GetValue(SubscriptionIdProperty); }
			set { base.SetValue(SubscriptionIdProperty, value); }
		}

		#endregion
		
		#region IComparable IEventActivity.QueueName { get; }

		private static readonly DependencyProperty QueueNameProperty = DependencyProperty.Register("QueueName", typeof(IComparable), typeof(WaitForTestRun));

		IComparable IEventActivity.QueueName
		{
			get { return (IComparable)this.GetValue(QueueNameProperty); }
		}

		#endregion



		void IEventActivity.Subscribe(ActivityExecutionContext parentContext, IActivityEventListener<QueueEventArgs> parentEventHandler)
		{
			if (null == parentContext)
				throw new ArgumentNullException("parentContext");

			if (null == parentEventHandler)
				throw new ArgumentNullException("parentEventHandler");

			this.IsEventActivity = true;

			IComparable queueName = ((IEventActivity)this).QueueName;

			WorkflowQueuingService svc = parentContext.GetService<WorkflowQueuingService>();
			svc.CreateWorkflowQueue(queueName, false).RegisterForQueueItemAvailable(parentEventHandler, this.QualifiedName);
			WorkflowQueue que = svc.GetWorkflowQueue(queueName);

			DateTime runAt = DateTime.UtcNow + TimeSpan.FromSeconds(1);
			TimerEventSubscription subscription = new TimerEventSubscription((Guid)queueName, this.WorkflowInstanceId, runAt);
			this.SubscriptionId = subscription.SubscriptionId;

			Activity root = (Activity)this.GetMonitoringWorkflow();
			((TimerEventSubscriptionCollection)root.GetValue(TimerEventSubscriptionCollection.TimerCollectionProperty)).Add(subscription);
			Thread.Sleep(1500);
		}

		void IEventActivity.Unsubscribe(ActivityExecutionContext parentContext, IActivityEventListener<QueueEventArgs> parentEventHandler)
		{
			if (null == parentContext)
				throw new ArgumentNullException("parentContext");

			if (null == parentEventHandler)
				throw new ArgumentNullException("parentEventHandler");

			WorkflowQueuingService svc = parentContext.GetService<WorkflowQueuingService>();
			WorkflowQueue queue = null;

			try
			{
				queue = svc.GetWorkflowQueue(this.SubscriptionId);
			}
			catch { }

			if (null != queue && queue.Count > 0)
			{
				object item = queue.Dequeue();
			}

			Activity root = (Activity)parentContext.Activity.GetMonitoringWorkflow();
			((TimerEventSubscriptionCollection)root.GetValue(TimerEventSubscriptionCollection.TimerCollectionProperty)).Remove(this.SubscriptionId);

			if (null != queue)
			{
				queue.UnregisterForQueueItemAvailable(parentEventHandler);
				svc.DeleteWorkflowQueue(this.SubscriptionId);
			}

			this.SubscriptionId = Guid.Empty;
		}

		void IActivityEventListener<QueueEventArgs>.OnEvent(object sender, QueueEventArgs e)
		{
			if (null == sender)
				throw new ArgumentNullException("sender");

			if (null == e)
				throw new ArgumentNullException("e");

			if (!(sender is ActivityExecutionContext))
				throw new ArgumentException("sender");

			ActivityExecutionContext context = (ActivityExecutionContext)sender;
			if(this.ExecutionStatus != ActivityExecutionStatus.Closed)
			{
				WorkflowQueuingService service = context.GetService<WorkflowQueuingService>();
				service.GetWorkflowQueue(e.QueueName).Dequeue();
				service.DeleteWorkflowQueue(e.QueueName);
				context.CloseActivity();
			}
		}

	}
}
